﻿namespace JoinBox.RemoteAccess;


using System;
using System.CodeDom.Compiler;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Text;
using Microsoft.CSharp;


// 动态编译的参考文章1 https://www.cnblogs.com/zlgcool/archive/2008/10/12/1309616.html
// 本质就是先编译生成一个dll,在创建程序域然后载入

public static class RemoteLoaderTool
{
    // 远程访问接口的dll,此处最好永远不变
    public const string RemoteAccessNamespace = "RemoteAccess";// dll名称+命名空间 修改的话要把这个也改了

    /// <summary>
    /// 获取dll运行路径
    /// </summary>
    /// <param name="full">是否获取文件名</param>
    /// <returns>当前dll路径</returns>     
    public static string GetAssemblyPath(bool full)
    {
        // 获取程序集的位置 "file:/// G:/K01.惊惊连盒/NET35/JoinBox.dll"
        string path = Assembly.GetExecutingAssembly().CodeBase;
        /*
         * 似乎直接切割比较快
         * path = path.Replace("file:/// ", string.Empty).Replace("/", "\\");
         */
        var url = new UriBuilder(path);
        path = Uri.UnescapeDataString(url.Path);// 这里路径是/

        if (full)
            return Path.GetFullPath(path); // 这里是\\
        path = Path.GetDirectoryName(path);// 这里是\\
        var it = path.LastIndexOf('\\');
        if (it != -1)
            path = path.Substring(0, it) + "\\";  // 返回上一级目录
        return path;
    }



    /// <summary>
    /// 动态编译
    /// </summary>
    /// <param name="code">要动态编译的代码</param>
    /// <param name="outputAssembly">生成的dll文件名带后缀,只要设置了这个就会生成文件</param>
    /// <returns></returns>
    public static string CSharpCode(StringBuilder code, string? outputAssembly = null)
    {
        // 2.通过抵消新的编译参数实例来设置运行时编译参数
        var cp = new CompilerParameters
        {
            GenerateInMemory = false,// 在内存中生成
        };
        if (outputAssembly is not null)
            cp.OutputAssembly = outputAssembly; // 程序集名称

        cp.ReferencedAssemblies.Add("System.dll");
        cp.ReferencedAssemblies.Add("System.Windows.Forms.dll");

        // 加载远程加载器接口
        // 如果没有这句,那么将导致无法和客户端的类进行通讯
        // 报错: 无法将类型为"客户端.HelloWorld"的对象强制转换为类型"RemoteAccess.IRemotelnterface"。
        cp.ReferencedAssemblies.Add(RemoteAccessNamespace + ".dll");

        // 编译
        var error = new StringBuilder();
        using (var comp = new CSharpCodeProvider())
        {
            var cr = comp.CompileAssemblyFromSource(cp, code.ToString());
            if (cr.Errors.HasErrors)
            {
                error.Append(Environment.NewLine + "动态编译错误:");
                error.Append(Environment.NewLine + cr.Errors.Count.ToString() + "错误的:");
                for (int x = 0; x < cr.Errors.Count; x++)
                {
                    error.Append(Environment.NewLine +
                        "行数: " + cr.Errors[x].Line.ToString() +
                        " ------- " +
                        cr.Errors[x].ErrorText);
                }
            }
        }
        return error.ToString();
    }

    /// <summary>
    /// 创建程序域然后卸载,瞬时生命周期
    /// </summary>
    /// <param name="name">新域的名称</param>
    /// <param name="action"><see cref="RemoteLoader"/>返回新域内的接口</param>
    public static void JAppDomainShort(string name, Action<RemoteLoader?> action)
    {
        var jAppDomain = new JAppDomain(name);
        try
        {
            action(jAppDomain.JRemoteLoader);
        }
        catch (Exception eeee)
        {
            Debug.WriteLine(eeee.Message);
        }
        finally
        {
            jAppDomain.Dispose();
        }
    }
}